package scu.maqiang.mesh;

import java.io.FileWriter;
import java.io.IOException;
import java.util.HashSet;

import scu.maqiang.numeric.Constants;
import scu.maqiang.numeric.MVO;
import scu.maqiang.numeric.SIMatrix;
import scu.maqiang.numeric.UnorderIntArray;

public class Mesh2Q8 extends Mesh {

	public Mesh2Q8() {
		super();
		nDim = 2;
		nPerElement = 8;
		nPerBoundary = 3;
		nBoundaryPerElement = 4;
		nEdgePerElement = 8;
		nEdgePerBoundary = 2;
		tecplotType = "FEQUADRILATERAL";
	}

	public Mesh2Q8 square(int xNum, int yNum) {
		int i, j, li, lj, lk;
		int[] t = new int[8];
		int nv = (3 * xNum + 2) * yNum + 2 * xNum + 1;
		int nt = xNum * yNum;
		int nb = 2 * (xNum + yNum);
		initMesh0(nv, nt, nb);
		int count  = 0;
		for(j = 0; j < yNum; j++) {
			for(i = 0; i < 2 * xNum + 1; i++) {
				nodes[count++] = new double[] {i / (2.0 * xNum), 1.0 * j / yNum};
			}
			for(i = 0; i < xNum + 1; i++) {
				nodes[count++] = new double[] {i * 1.0 / xNum, (2 * j + 1.0) / (2 *yNum)};
			}
		}

		for(i = 0; i < 2 * xNum + 1; i++) {
			nodes[count++] = new double[] {i /(2.0 * xNum), 1.0};
		}

		count = 0;
		for(j = 0; j < yNum; j++) {
			for(i = 0; i < xNum; i++) {
				t[0] = j * (3 * xNum + 2) + 2 * i;
				t[1] = t[0] + 2;
				t[2] = t[1] + 3 * xNum + 2;
				t[3] = t[2] - 2;
				t[4] = t[0] + 1;
				t[6] = t[2] - 1;
				t[7] = j * (3 * xNum + 2) + 2 * xNum + 1 + i;
				t[5] = t[7] + 1;
				elements[count++] = t.clone();
			}
		}

		count = 0;
		for(i = 0; i < xNum; i++) {
			li = 2 * i;
			lj = li + 2;
			lk = li + 1;
			boundaries[count] = new int[] {li, lj, lk};
			boundaryLabel[count++]  = 1;
		}

		for(i = 0; i < yNum; i++) {
			li = i * (3 * xNum + 2) + 2 * xNum;
			lj = li + 3 * xNum + 2;
			lk = li + xNum + 1;
			boundaries[count] = new int[] {li, lj, lk};
			boundaryLabel[count++] = 2;
		}

		for(i = 0; i < xNum; i++) {
			li = nv - 1 - 2 * i;
			lj = li - 2;
			lk = li - 1;
			boundaries[count] = new int[] {li, lj, lk};
			boundaryLabel[count++] = 3;
		}

		for(i = 0; i < yNum; i++) {
			li = i * (3 * xNum + 2);
			lj = li + 3 * xNum + 2;
			lk = li + 2 * xNum + 1;
			boundaries[count] = new int[] {lj, li, lk};
			boundaryLabel[count++] =  4;
		}
		return this;
	}

	public Mesh2Q8 fromMesh2DQ4(Mesh2Q4 mshR) {
		SIMatrix C = mshR.renumberingEdges();
		int sizeC = C.getNNZ();
		int[] idxI = new int[sizeC];
		int[] idxJ = new int[sizeC];
		int[] value = new int[sizeC];
		int[] number = new int[sizeC];
		C.getTriValues(idxI, idxJ, value);
		for (int i = 0; i < sizeC; i++) {
			number[i] = i;
		}

		nv = sizeC + mshR.nv;
		nodes = new double[nv][];
		nodeLabel = new int[nv];
		int[][] edges = new int[mshR.nt][4];
		for (int i = 0, n = mshR.getNt(); i < n; i++) {
			int[] tri = mshR.elements[i];
			int d1 = tri[0];
			int d2 = tri[1];
			for (int j = 0; j < sizeC; j++) {
				if ((idxI[j] == d1 && idxJ[j] == d2) || (idxI[j] == d2 && idxJ[j] == d1)) {
					edges[i][0] = j + mshR.nv;
					break;
				}
			}
			d1 = tri[1];
			d2 = tri[2];
			for (int j = 0; j < sizeC; j++) {
				if ((idxI[j] == d1 && idxJ[j] == d2) || (idxI[j] == d2 && idxJ[j] == d1)) {
					edges[i][1] = j + mshR.nv;
					break;
				}
			}
			d1 = tri[2];
			d2 = tri[3];
			for (int j = 0; j < sizeC; j++) {
				if ((idxI[j] == d1 && idxJ[j] == d2) || (idxI[j] == d2 && idxJ[j] == d1)) {
					edges[i][2] = j + mshR.nv;
					break;
				}
			}

			d1 = tri[3];
			d2 = tri[0];
			for (int j = 0; j < sizeC; j++) {
				if ((idxI[j] == d1 && idxJ[j] == d2) || (idxI[j] == d2 && idxJ[j] == d1)) {
					edges[i][3] = j + mshR.nv;
					break;
				}
			}

		}

		for (int i = 0; i < mshR.nv; i++) {
			nodes[i] = mshR.nodes[i];
		}
		for (int i = mshR.nv; i < nv; i++) {
			nodes[i] = new double[nDim];
		}
		int idx;
		nt = mshR.getNt();
		elements = new int[nt][];
		elementLabel = new int[nt];
		for (int i = 0, n = mshR.getNt(); i < n; i++) {
			int[] quad4 = mshR.elements[i];
			double[][] node = new double[4][];
			for (int j = 0; j < 4; j++) {
				node[j] = mshR.nodes[quad4[j]];
			}
			idx = edges[i][0];
			double[] newNode = new double[] {0.5 * (node[0][0] + node[1][0]), 0.5 * (node[0][1] + node[1][1])};
			nodes[idx] = newNode;
			idx = edges[i][1];
			newNode = new double[] {0.5 * (node[1][0] + node[2][0]), 0.5 * (node[1][1] + node[2][1])};
			nodes[idx] = newNode;
			idx = edges[i][2];
			newNode = new double[] {0.5 * (node[2][0] + node[3][0]), 0.5 * (node[2][1] + node[3][1])};
			nodes[idx] = newNode;
			idx = edges[i][3];
			newNode = new double[] {0.5 * (node[3][0] + node[0][0]), 0.5 * (node[3][1] + node[0][1])};
			nodes[idx] = newNode;
			// ����µ�Ԫ
			int[] quad8Idx = new int[] { quad4[0], quad4[1], quad4[2], quad4[3], edges[i][0], edges[i][1], edges[i][2], edges[i][3]};
			elements[i] = quad8Idx;
			elementLabel[i] = mshR.elementLabel[i];
		}

		nb = mshR.nb;
		boundaries = new int[nb][];
		boundaryLabel = new int[nb];
		int d1, d2;
		for (int i = 0; i < nb; i++) {
			int[] li = mshR.boundaries[i];
			d1 = li[0];
			d2 = li[1];
			for (int j = 0; j < sizeC; j++) {
				if ((idxI[j] == d1 && idxJ[j] == d2) || (idxI[j] == d2 && idxJ[j] == d1)) {
					boundaries[i] = new int[] {d1, d2, j + mshR.nv};
					boundaryLabel[i] = mshR.boundaryLabel[i];
					break;
				}
			}
		}
		return this;
	}


	@Override
	public double[] getinteriorPointInElement(double[][] coord) {
		double[] x = new double[4];
		double[] y = new double[4];
		for(int i = 0; i < 4; i++) {
			x[i] = coord[i][0];
			y[i] = coord[i][1];
		}
		return new double[]{ MVO.average(x), MVO.average(y) };
	}

	@Override
	public double[] getinteriorPointOnBoundary(double[][] coord) {
		return coord[2].clone();
	}

	@Override
	public int[][] getBoundarysFromElement(int i) {
		int[][] boundarys = new int[nBoundaryPerElement][];
		int[] ele = elements[i];
		boundarys[0] = new int[] {ele[0], ele[1], ele[4]};
		boundarys[1] = new int[] {ele[1], ele[2], ele[5]};
		boundarys[2] = new int[] {ele[2], ele[3], ele[6]};
		boundarys[3] = new int[] {ele[3], ele[0], ele[7]};
		return boundarys;
	}

	@Override
	public boolean checkMesh(double[][] coord) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public int getTecplotNt() {
		// TODO Auto-generated method stub
		return nt;
	}

	@Override
	public int[][] getEdgesFromElement(int i) {
		int[][] result = new int[8][];
		int[] ele = elements[i];
		result[0] = new int[] {ele[0], ele[4]};
		result[1] = new int[] {ele[4], ele[1]};
		result[2] = new int[] {ele[1], ele[5]};
		result[3] = new int[] {ele[5], ele[2]}; 
		result[4] = new int[] {ele[2], ele[6]};
		result[5] = new int[] {ele[6], ele[3]};
		result[6] = new int[] {ele[3], ele[7]};
		result[7] = new int[] {ele[7], ele[0]};
		return result;
	}
	
//	@Override
//	public void toTecplot(String fileName) {
//		try (FileWriter fw = new FileWriter(fileName)){
//			fw.write(Tecplot.meshFileString + "\n");
//			fw.write(Tecplot.dimVariableString[nDim - 1] + "\n");
//
//			fw.write(Tecplot.ZoneString(this) + "\n");
//			for (Node node : nodes) {
//				fw.write(node + "\n");
//			}
//
//			writeElements(fw);
//			fw.flush();
//			fw.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}
//
//	@Override
//	public void toTecplot(String fileName, double[] x) {
//		try (FileWriter fw = new FileWriter(fileName)){
//			fw.write("title = mesh file\n");
//			fw.write("variables= \"x\", \"y\", \"u\"\n");
//
//			fw.write(Tecplot.ZoneString(this) + "\n");
//			int idx = 0;
//			for (Node node : nodes) {
//				fw.write(node + "\t" + x[idx++] + "\n");
//			}
//			writeElements(fw);
//			fw.flush();
//			fw.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}
//
//	@Override
//	public void toTecplot(String fileName, double[][] mat) {
//		int row = mat.length;
//		int j = 0;
//		try (FileWriter fw = new FileWriter(fileName)){
//			fw.write("title = solution file\n");
//			fw.write("variables= \"x\", \"y\"");
//			for(int i = 0; i < row; i++) {
//				fw.write(", \"u" + (i + 1) + "\"");
//			}
//			fw.write("\n");
//
//			fw.write(Tecplot.ZoneString(this) + "\n");
//			for (Node node : nodes) {
//				fw.write(node + "\t");
//				for(int i = 0; i < row; i++) {
//					fw.write(mat[i][j] + "\t");
//				}
//				j++;
//				fw.write("\n");
//			}
//
//			writeElements(fw);
//			fw.flush();
//			fw.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}		
//	}
//
//	@Override
//	public void toTecplot(String fileName, double time, double[] x) {
//		try (FileWriter fw = new FileWriter(fileName)){
//			if (Math.abs(time) < Constants.Er) {
//				fw.write("title = solution file\n");
//				fw.write("variables= \"x\", \"y\", \"u\"\n");
//				fw.write(Tecplot.ZoneStringTime(this) + time + "\n");
//				int i = 0;
//				for (Node node : nodes) {
//					fw.write(node + "\t" + x[i++] + "\n");
//				}
//				
//				writeElements(fw);
//				fw.flush();
//				fw.close();
//			} else {
//				fw.write(Tecplot.ZoneStringShareTime(this) + time + "\n");
//				for (double xx : x) {
//					fw.write(xx + "\n");
//				}
//				fw.flush();
//				fw.close();
//			}
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//	}
//
//	@Override
//	public void toTecplot(String fileName, double time, double[][] mat) {
//		int row = mat.length;
//		try (FileWriter fw = new FileWriter(fileName)){
//			if (Math.abs(time) < Constants.Er) {
//				fw.write("title = mesh file\n");
//				fw.write("variables= \"x\", \"y\"");
//				for(int j = 0; j < row; j++) {
//					fw.write(", \"u" + (j + 1) + "\"");
//				}
//				fw.write("\n");				
//
//				fw.write(Tecplot.ZoneString(this) + time + "\n");
//				int j = 0;
//				for (Node node : nodes) {
//					fw.write(node + "\t");
//					for (int i = 0; i < row; i++) {
//						fw.write(mat[i][j] + "\t");
//					}
//					j++;
//					fw.write("\n");
//				}
//				
//
//				writeElements(fw);
//				fw.flush();
//				fw.close();
//			} else {
//				fw.write(Tecplot.ZoneStringShareTime(this) + time + "\n");
//				int col = mat[0].length;
//				for(int i = 0; i < row; i++) {
//					for(int j = 0; j < col; j++) {
//						fw.write(mat[i][j] + "\t");
//					}
//					fw.write("\n");
//				}
//				fw.flush();
//				fw.close();
//			}
//		} catch (Exception e) {
//			e.printStackTrace();
//		}		
//	}
//
//	private void writeElements(FileWriter fw) throws IOException{
//		for (Element tri : elements) {
//			int[] idx = tri.idx;
//			fw.write((idx[0] + 1) + "\t" + (idx[1] + 1) + "\t" + (idx[2] + 1) + "\t" + (idx[3] + 1) + "\n");
//		}
//	}

}
